package com.longsys.framework.eventsourcing.sbclient.core;

import com.longsys.eventsourcing.core.EventAggregate;
import com.longsys.eventsourcing.core.EventUser;
import org.springframework.context.ApplicationContext;

import java.lang.reflect.Field;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.Date;
import java.util.UUID;

public class EventStoreClient<T extends Event, B extends Aggregate> {

    private  ApplicationContext applicationContext;

    private  EventAggregateMsgProducer eventAggregateMsgProducer;

    private  String system;

    public EventStoreClient() {
    }

    public EventStoreClient(ApplicationContext applicationContext, EventAggregateMsgProducer eventAggregateMsgProducer, String system) {
        this.applicationContext = applicationContext;
        this.eventAggregateMsgProducer = eventAggregateMsgProducer;
        this.system = system;
    }

    /**
     * 存储
     * @param eventClass
     * @param aggregateClass
     * @param id
     */
    public void store(Class<T> eventClass, Class<B> aggregateClass, Object id) {
        store(eventClass, aggregateClass, id, new EventUser());
    }

    /**
     * 存储
     * @param eventClass
     * @param aggregateClass
     * @param id
     */
    public void store(Class<T> eventClass, Class<B> aggregateClass, Object id, EventUser eventUser) {
        //从sping容器中获取聚合的资源库
        String[] aggregateRepositoryBeanNames = applicationContext.getBeanNamesForType(AggregateRepository.class);
        AggregateRepository<B> targetAggregateRepository = null;
        for (String aggregateRepositoryBeanName : aggregateRepositoryBeanNames) {
            Object aggregateRepository = applicationContext.getBean(aggregateRepositoryBeanName);
            Class aggregateRepositoryClass = aggregateRepository.getClass();

            while (containsInterface(aggregateRepositoryClass.getSuperclass(), AggregateRepository.class)) {
                aggregateRepositoryClass = aggregateRepositoryClass.getSuperclass();
            }

            Type targetType = null;
            Type[] types = aggregateRepositoryClass.getGenericInterfaces();
            for (Type type : types) {
                ParameterizedType pType = (ParameterizedType) type;
                Type actualTypeArgument = pType.getActualTypeArguments()[0];
                boolean flag = actualTypeArgument.getTypeName().equals(Aggregate.class.getTypeName())||
                        containsInterface((Class)actualTypeArgument, Aggregate.class);
                if (flag) {
                    targetType = type;
                }
            }

            if (targetType == null) {
                throw new RuntimeException("未找到AggregateRepository对应的Aggregate");
            }

            ParameterizedType p = (ParameterizedType) targetType;
            Class aggrClass = (Class) p.getActualTypeArguments()[0];
            if (aggregateClass == aggrClass) {
                targetAggregateRepository = (AggregateRepository) aggregateRepository;
            }
        }

        if (targetAggregateRepository == null) {
            throw new RuntimeException("没有找到 Aggregate 对应的 AggregateRepository");
        }

        //调用资源库获取聚合对象
        Aggregate aggregate = targetAggregateRepository.findById(id);

        //构建 EventAggregate
        EventAggregate eventAggregate = new EventAggregate(
                UUID.randomUUID().toString(),
                system,
                eventClass.getSimpleName(),
                aggregateClass.getSimpleName(),
                new Date(),
                eventUser,
                null);

        Field[] declaredFields = aggregateClass.getDeclaredFields();
        for (Field declaredField : declaredFields) {
            try {
                declaredField.setAccessible(true);
                String fieldName = declaredField.getName();
                Object fieldValue = declaredField.get(aggregate);
                eventAggregate.addAggregateInfo(fieldName, fieldValue);
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            }
        }

        //将EventAggregate发送到Kafka中
        eventAggregateMsgProducer.send(eventAggregate);
    }

    private boolean containsInterface(Class clazz, Class aInterface) {
        for (Class oneInterface : clazz.getInterfaces()) {
            if (oneInterface == aInterface) {
                return true;
            }
        }
        return false;
    }

}
